#include "../include/mouse.h"
#include "../include/kernel.h"
#include "../include/stdio.h"
#include "../include/pantalla.h"
#include "../include/string.h"
#include "../include/teclado.h"

/*
 * Inicializa el mouse por hardware y setea las variables que se van a usar
 */
void inicializarMouse()
{
	int status_byte;

	// serie de comandos que hay que enviar al mouse para inicializarlo y que empiece a enviar paquetes
	mouse_wait(1);
	outportb(0x64,0xA8);

	mouse_wait(1);
	outportb(0x64,0x20);

	mouse_wait(0);
	status_byte = (inportb(0x60) | 2);

	mouse_wait(1);
	outportb(0x64, 0x60);

	mouse_wait(1);
	outportb(0x60, status_byte);

	mouse_write(0xF6);
	mouse_read();

	mouse_write(0xF4);
	mouse_read();

	copiando=0;					// estado inicial: no esta copiando, el buffer vacio
	bufferMouse[0]=0;			// y situado en el centro de la pantalla
	posMouse.x=80;
	posMouse.y=72;
	posPantallaActual.x=40;
	posPantallaActual.y=12;
	setMouse();					// dibuja el mouse
}

/*
 * Procesa el codigo recibido en cada paquete del mouse (3 paquetes por accion)
 */
void procMouse(unsigned char code)
{
	int aux=0,i,j,izq=0,der=0;;
	static unsigned char cycle = 0;
	static char mouse_bytes[3];
	Point delta={0,0};

	mouse_bytes[cycle++] = code;	// cada accion del mouse genera 3 paquetes/interrupciones
	if (cycle == 3)					// hay que tener las 3 para poder procesar la accion
	{
		delta.x=mouse_bytes[1];		// en el byte 1 esta el desplazamiento en X (puede ser negativo)
		delta.y=mouse_bytes[2]*-1;	// en el byte 2 esta el desplazamiento en Y (puede ser negativo)
									// se invierte ya que el deltaY leido es positivo hacia arriba y
									// en coordenadas es al reves

		cycle = 0;

		if ((mouse_bytes[0] & 0x80) || (mouse_bytes[0] & 0x40))
			return; // Hubo algun error y descartamos los paquetes
		der=mouse_bytes[0] & 0x2;	// los primeros dos bits del byte 0 indican el estado de los botones
		izq=mouse_bytes[0] & 0x1;	// izquierdo y derecho

		reactivarSalvaPantalla();

		if (!mouseEnSeleccion())	// borro el mouse salvo que este situado sobre el recuadro de seleccion
			unsetMouse();			// para luego redibujarlo cuando se cambio la posicion

		// si el mouse no se fue de los limites
		if ((delta.x<0 && posMouse.x+delta.x>=0) || (delta.x>=0 && posMouse.x+delta.x<=(COLUMNAS-1)*ESCALA_MOUSE_X))
		{
			posMouse.x+=delta.x;							// cambio X del mouse
			posPantallaActual.x=posMouse.x/ESCALA_MOUSE_X;	// cambio X escalada en modo texto del mouse
		}
		// si el mouse no se fue de los limites
		if ((delta.y<0 && posMouse.y+delta.y>=0) || (delta.y>=0 && posMouse.y+delta.y<=(FILAS-1)*ESCALA_MOUSE_Y))
		{
			posMouse.y+=delta.y;							// cambio Y del mouse
			posPantallaActual.y=posMouse.y/ESCALA_MOUSE_Y;	// cambio Y escalada en modo texto del mouse
		}
		if ( izq && ! copiando )	// empiezo a seleccionar
		{
			unsetSeleccionMouse();	// borro la anterior seleccion

			copiando=1;
			posSeleccionInicial=posPantallaActual;	// asigno la coordenadas inicial y final de seleccion
			posSeleccionFinal=posPantallaActual;
		}
		else if ( izq && copiando )	// estoy arrastrando el mouse
		{
			unsetSeleccionMouse();					// borro seleccion anterior
			posSeleccionFinal=posPantallaActual;	// posiciono la coordenada final
			setSeleccionMouse();					// redibujo la seleccion
		}
		else if ( ! izq && copiando ) // estaba copiando y deje de copiar
		{
			// ordeno las posiciones para que la inicial sea menor a la final en ambas coordenadas
			ordenar(&posSeleccionInicial,&posSeleccionFinal);

			// recorro la seleccion y guardo los caracteres unicamente en el buffer
			for (i=posSeleccionInicial.y;i<=posSeleccionFinal.y;i++)
				for (j=posSeleccionInicial.x;j<=posSeleccionFinal.x;j++)
					bufferMouse[aux++]=pantalla->dir[(j*2)+(i*COLUMNAS*2)];

			bufferMouse[aux]=0;
			copiando=0;
		}
		else if ( der )	// pegar
		{
			for (i=0;i<strlen(bufferMouse);i++) // proceso el buffer como si hubiera sido tecleado
				procAscii(bufferMouse[i]);
		}

		setMouse(); // vuelvo a dibujar el mouse en la nueva posicion
	}
}

/*
 * Realiza los swaps si son necesarios para que la coordenada inicial sea inferior a la final en X e Y
 */
void ordenar(Point *inicial, Point *final)
{
	if (inicial->x > final->x)
		swap(&(inicial->x),&(final->x));

	if (inicial->y > final->y)
		swap(&(inicial->y),&(final->y));
}

/*
 * Dibuja el rectangulo de seleccion tomando las coordenadas inicial y final globales
 */
void setSeleccionMouse()
{
	asignarAtributoArea(posSeleccionInicial,posSeleccionFinal,FONDO_MOUSE);
}

/*
 * Borra el rectangulo de seleccion tomando las coordenadas inicial y final globales
 */
void unsetSeleccionMouse()
{
	asignarAtributoArea(posSeleccionInicial,posSeleccionFinal,WHITE_TXT);
}

/*
 * Intercambia dos variables
 */
void swap(int *n1, int *n2)
{
	int aux;

	aux=*n1;
	*n1=*n2;
	*n2=aux;
}

/*
 * Dada un area determinada por dos coordenadas setea el byte de atributo de cada posicion de pantalla
 * en ese area
 */
void asignarAtributoArea(Point inicial,Point final,char atr)
{
	int i,j;

	// ordeno las posiciones para que la inicial sea menor a la final en ambas coordenadas
	ordenar(&inicial,&final);

	if (final.x<0 || final.y<0)
		return;

	if (inicial.x<0)
		inicial.x=0;

	if (inicial.y<0)
		inicial.y=0;

	for (i=inicial.x;i<=final.x;i++)
		for (j=inicial.y;j<=final.y;j++)
			asignarAtributoPos(i,j,atr);
}

/*
 * Setea el byte de atributo de una posicion de pantalla dando las coordenadas
 */
void asignarAtributoPos(int col, int fila, char atr)
{
	// mantengo el blink si estaba activo
	//pantalla->dir[(col*2)+(fila*COLUMNAS*2)+1] &= BLINK;
	pantalla->dir[(col*2)+(fila*COLUMNAS*2)+1] = atr;
}

/*
 * Devuelve si la coordenada del mouse esta incluida en el area de seleccion actual
 */
int mouseEnSeleccion()
{
	return (posPantallaActual.x>=posSeleccionInicial.x && posPantallaActual.x<=posSeleccionFinal.x && posPantallaActual.y>=posSeleccionInicial.y && posPantallaActual.y<=posSeleccionFinal.y);
}

/*
 * Dibuja el mouse
 */
void setMouse()
{
	asignarAtributoPos(posPantallaActual.x,posPantallaActual.y,FONDO_MOUSE);
	//pantalla->dir[(posPantallaActual.x*2)+(posPantallaActual.y*COLUMNAS*2)+1] &= BLINK;
	//pantalla->dir[(posPantallaActual.x*2)+(posPantallaActual.y*COLUMNAS*2)+1] |= FONDO_MOUSE;
}

/*
 * Borra el mouse
 */
void unsetMouse()
{
	asignarAtributoPos(posPantallaActual.x,posPantallaActual.y,WHITE_TXT);
	//pantalla->dir[(posPantallaActual.x*2)+(posPantallaActual.y*COLUMNAS*2)+1] &= BLINK;
	//pantalla->dir[(posPantallaActual.x*2)+(posPantallaActual.y*COLUMNAS*2)+1] |= WHITE_TXT;
}

/*
 * Funcion auxiliar para inicializar el mouse
 */
unsigned char mouse_read()
{
	mouse_wait(0);
	return inportb(0x60);
}

/*
 * Funcion auxiliar para inicializar el mouse
 */
void mouse_write(unsigned char a_write)
{
	mouse_wait(1);
	outportb(0x64, 0xD4);
	mouse_wait(1);
	outportb(0x60, a_write);
}

/*
 * Funcion auxiliar para inicializar el mouse
 */
void mouse_wait(unsigned char type)
{
	unsigned int _time_out=100000;

	if(type==0)
	{
		while(_time_out--)
		{
			if((inportb(0x64) & 1)==1)
				return;
		}
	}
	else
	{
		while(_time_out--)
		{
			if((inportb(0x64) & 2)==0)
				return;
		}
	}
}
